/**  
 * @Title:  HZQProxy.java   
 * @Package Dongtai   
 * @Description:    TODO   
 * @author: hezhiqin     
 * @date:   2019年4月2日 上午10:06:17   
 */  
package Dongtai;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

/**   
 * @ClassName:  HZQProxy   
 * @Description:TODO(用来生成源代码的工具类)   
 * @author: hezhiqin
 * @date:   2019年4月2日 上午10:06:17   
 *      
 */
public class HZQProxy {
	
	
	public static final String ln = "\r\n";
	
	public static Object newProxyInstance(HZQClassLoader classLoader,Class<?> [] interfaces,
			HZQInvocationHandler h) {
				try {
					//1、动态生成源代码.java 文件
					String src = generateSrc(interfaces);
					System.out.println(src);
					//2、Java 文件输出磁盘
					String filePath = HZQProxy.class.getResource("").getPath();
					File f = new File(filePath + "$Proxy0.java");
					FileWriter fw = new FileWriter(f);
					fw.write(src);
					fw.flush();
					fw.close();
					
					//3、把生成的.java 文件编译成.class 文件
					JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
					StandardJavaFileManager manage = compiler.getStandardFileManager(null,null,null);
					Iterable iterable = manage.getJavaFileObjects(f);
					
					JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null,null,null,iterable);
					task.call();
					manage.close();
					
					
					//4、编译生成的.class 文件加载到JVM 中来
					Class proxyClass = classLoader.findClass("$Proxy0");
					Constructor c = proxyClass.getConstructor(HZQInvocationHandler.class);
					f.delete();
					

					return c.newInstance(h);//5、返回字节码重组以后的新的代理对象
					
				} catch (Exception e) {
					e.printStackTrace();
				}
				
				return null;
	}

	/**   
	 * @Title: generateSrc   
	 * @Description: TODO(动态生成java文件)   
	 * @param: @param interfaces
	 * @param: @return      
	 * @return: String      
	 * @throws 
	 * @author: hezhiqin
	 * @date: 2019年4月2日 上午10:34:04  
	 */
	private static String generateSrc(Class<?>[] interfaces) {
		StringBuffer sb = new StringBuffer();
		sb.append("package Dongtai;" + ln);
		sb.append("import Person.Person;" + ln);
		sb.append("import java.lang.reflect.*;" + ln);
		sb.append("public class $Proxy0 implements " + interfaces[0].getSimpleName() + "{" + ln);
			sb.append("HZQInvocationHandler h;" + ln);
			sb.append("public $Proxy0(HZQInvocationHandler h) { " + ln);
				sb.append("this.h = h;");
			sb.append("}" + ln);
			for (Method m : interfaces[0].getMethods()){
				
				Class<?>[] params = m.getParameterTypes();
				StringBuffer paramNames = new StringBuffer();
				StringBuffer paramValues = new StringBuffer();
				StringBuffer paramClasses = new StringBuffer();
				
				for (int i = 0; i < params.length; i++) {
					Class clazz = params[i];
					String type = clazz.getName();
					String paramName = toLowerFirstCase(clazz.getSimpleName());
					paramNames.append(type + " " + paramName);
					paramValues.append(paramName);
					paramClasses.append(clazz.getName() + ".class");
					if(i > 0 && i < params.length-1){
						paramNames.append(",");
						paramClasses.append(",");
						paramValues.append(",");
					}
				}
				sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(" +
						paramNames.toString() + ") {" + ln);
					sb.append("try{" + ln);
						sb.append("Method m = " + interfaces[0].getSimpleName() + ".class.getMethod(\""
							+ m.getName() + "\",new Class[]{" + paramClasses.toString() + "});" + ln);
						sb.append((hasReturnValue(m.getReturnType()) ? "return " : "") +
								getCaseCode("this.h.invoke(this,m,new Object[]{" + paramValues + "})",m.getReturnType()) + ";" + ln);
					sb.append("}catch(Error _ex) { }");
					sb.append("catch(Throwable e){" + ln);
					sb.append("throw new UndeclaredThrowableException(e);" + ln);
					sb.append("}");
					sb.append(getReturnEmptyCode(m.getReturnType()));
				sb.append("}" + ln);
				
			}
		sb.append("}" + ln);
		return sb.toString();
	}
	
	private static Map<Class,Class> mappings = new HashMap<Class, Class>();
	
	static {
		mappings.put(int.class,Integer.class);
	}
	
	
	private static String getCaseCode(String code,Class<?> returnClass){
		if(mappings.containsKey(returnClass)){
			return "((" + mappings.get(returnClass).getName() + ")" + code + ")." +
			returnClass.getSimpleName() + "Value()";
		}
		
		return code;
	}
	
	
	private static String getReturnEmptyCode(Class<?> returnClass){
		if(mappings.containsKey(returnClass)){
			return "return 0;";
		}else if(returnClass == void.class){
			return "";
		}else {
			return "return null;";
		}
	}
	
	//第一个字母转小写
	private static String toLowerFirstCase(String src){
		char [] chars = src.toCharArray();
		chars[0] += 32;
		return String.valueOf(chars);
	}
	
	private static boolean hasReturnValue(Class<?> clazz){
		return clazz != void.class;
	}

}
